using System.Runtime.CompilerServices;
using Pinwheel.LSerialize;

namespace Pinwheel.Kernel
{
    public class SocketBuffer
    {
        public const int DefaultSize = 128 * 1024;          // 128KB
        public const int DefaultSizeByte = 2;

        private byte[] m_buffer;
        public byte[] Buffer => m_buffer;
        public int MaxSize => m_buffer.Length;
        
        private int m_head;
        public int Head => m_head;
        public int ReadCount => m_head <= m_tail ? m_tail - m_head : MaxSize - m_head;
        
        private int m_tail;
        public int Tail => m_tail;
        public int WriteCount => m_tail < m_head ? m_head - m_tail : MaxSize - m_tail;

        private int m_curSize;
        public int CurSize => m_curSize;
        public int Free => MaxSize - m_curSize;

        public int m_sizeByte;
        public int SizeByte => m_sizeByte;

        private SpinLock m_spinLock = new SpinLock();       // 自旋锁

        public SocketBuffer() : this(DefaultSize)
        {
        }

        public SocketBuffer(int maxSize, int sizeByte = DefaultSizeByte)
        {
            m_buffer = new byte[maxSize];
            m_sizeByte = sizeByte;
            m_head = 0;
            m_tail = 0;
            m_curSize = 0;
        }

        #region Read
        
        public void Rollback(int size)
        {
            bool lockCtrl = false;
            try
            {
                m_spinLock.Enter(ref lockCtrl);            // 进入自旋锁
                RollbackUnsafe(size);
            }
            finally
            {
                if (lockCtrl) m_spinLock.Exit();           // 退出自旋锁
            }
        }
        
        public void RollbackUnsafe(int size)
        {
            Assert.Check(size <= Free, $"Try push data with size {size} > {Free}");
            m_head -= size;
            if (m_head < 0) m_head += MaxSize;              // 环形更新头指针
            m_curSize += size;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public byte ReadByteUnsafe()
        {
            byte type = m_buffer[m_head];
            m_head += 1;
            if (m_head >= MaxSize) m_head -= MaxSize;       // 环形更新头指针
            m_curSize -= 1;
            return type;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int ReadSize()
        {
            bool lockCtrl = false;
            try
            {
                m_spinLock.Enter(ref lockCtrl);             // 进入自旋锁
                return ReadSizeUnsafe();
            }
            finally
            {
                if (lockCtrl) m_spinLock.Exit();            // 退出自旋锁
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private int ReadSizeUnsafe()
        {
            if (m_curSize < m_sizeByte) return 0;
            int size = 0;
            for (int i = 0; i < m_sizeByte; i++)
            {
                size |= m_buffer[m_head] << (8 * i);
                m_head += 1;
                if (m_head >= MaxSize) m_head -= MaxSize;   // 环形更新头指针 
            }
            m_curSize -= m_sizeByte;
            Assert.Check(size > 0, $"Read size {size} <= 0, {m_head} {m_tail} {m_curSize}");
            return size;
        }
        
        public int ReadBuffer(byte[] data, int index, int size)
        {
            bool lockCtrl = false;
            try
            {
                m_spinLock.Enter(ref lockCtrl);             // 进入自旋锁
                return ReadBufferUnsafe(data, index, size);
            }
            finally
            {
                if (lockCtrl) m_spinLock.Exit();            // 退出自旋锁
            }
        }

        public int ReadBufferUnsafe(byte[] data, int index, int size)
        {
            // Log.Info($"[SocketBuffer] ReadBufferUnsafe: {m_head} {m_tail} {size} {m_curSize}");
            if (size > m_curSize) size = m_curSize;         // 防止溢出
                
            int firstPart = Math.Min(size, MaxSize - m_head);
            if (data != null!)
            {
                Array.Copy(m_buffer, m_head, data, index, firstPart);
            }
            m_head += firstPart;
            if (m_head >= MaxSize) m_head -= MaxSize;       // 环形更新头指针
                
            if (size > firstPart)
            {
                if (data != null!)
                {
                    Array.Copy(m_buffer, m_head, data, index + firstPart, size - firstPart);
                }
                m_head += size - firstPart;
                if (m_head >= MaxSize) m_head -= MaxSize;   // 环形更新头指针
            }
            m_curSize -= size;
            return size; 
        }

        public int Pop(ref byte[] data, int index)
        {
            bool lockCtrl = false;
            try
            {
                m_spinLock.Enter(ref lockCtrl);             // 进入自旋锁
                
                if (m_curSize < m_sizeByte) return 0;
                int size = ReadSizeUnsafe();                // 读取大小
                if (size == 0 || size > m_curSize)
                {
                    RollbackUnsafe(m_sizeByte);
                    return 0;
                }
                
                if (data == null!) data = new byte[size];
                if (data.Length < size) Array.Resize(ref data, size);
                ReadBufferUnsafe(data, index, size);
                
                return m_sizeByte + size;                   // size + data
            }
            finally
            {
                if (lockCtrl) m_spinLock.Exit();            // 退出自旋锁
            }
        }
        
        public void Clear(int size)
        {
            bool lockCtrl = false;
            try
            {
                m_spinLock.Enter(ref lockCtrl);             // 进入自旋锁
                Assert.Check(size <= m_curSize, $"Try clear data with size {size} > {m_curSize}");
                m_head += size;
                if (m_head >= MaxSize) m_head -= MaxSize;   // 环形更新头指针
                m_curSize -= size;
                MoveHead();
            }
            finally
            {
                if (lockCtrl) m_spinLock.Exit();            // 退出自旋锁
            }
        }

        #endregion

        

        #region Write

        public void Skip(int size)
        {
            bool lockCtrl = false;
            try
            {
                m_spinLock.Enter(ref lockCtrl);            // 进入自旋锁
                SkipUnsafe(size);
            }
            finally
            {
                if (lockCtrl) m_spinLock.Exit();            // 退出自旋锁
            }
        }

        public void SkipUnsafe(int size)
        {
            Assert.Check(size <= Free, $"Try push data with size {size} > {Free}");
            m_tail += size;
            if (m_tail >= MaxSize) m_tail -= MaxSize;       // 环形更新尾指针
            m_curSize += size;
            MoveHead();
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private int WriteByteUnsafe(byte type)
        {
            m_buffer[m_tail] = type;
            m_tail += 1;
            if (m_tail >= MaxSize) m_tail = 0;              // 环形更新尾指针
            m_curSize += 1;
            return 1;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int WriteSize(int size)
        {
            bool lockCtrl = false;
            try
            {
                m_spinLock.Enter(ref lockCtrl);             // 进入自旋锁
                if (m_sizeByte > Free) return 0;
                return WriteSizeUnsafe(size);
            }
            finally
            {
                if (lockCtrl) m_spinLock.Exit();            // 退出自旋锁
            }
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private int WriteSizeUnsafe(int size)
        {
            if (m_sizeByte > Free) return 0;
            for (int i = 0; i < m_sizeByte; i++)
            {
                m_buffer[m_tail] = (byte) (size >> (8 * i));
                m_tail += 1;
                if (m_tail >= MaxSize) m_tail = 0;          // 环形更新尾指针 
            }
            m_curSize += m_sizeByte;
            return m_sizeByte;
        }
        
        public void WriteBuffer(byte[] data, int index, int size)
        {
            bool lockCtrl = false;
            try
            {
                m_spinLock.Enter(ref lockCtrl);             // 进入自旋锁
                WriteBufferUnsafe(data, index, size);
            }
            finally
            {
                if (lockCtrl) m_spinLock.Exit();            // 退出自旋锁
            }
        }
        
        public void WriteBufferUnsafe(byte[] data, int index, int size)
        {
            Assert.Check(null! != data, $"Try push null data to buffer");
            Assert.Check(size <= Free, $"Try push data with size {size} > {Free}");
                
            int firstPart = Math.Min(size, MaxSize - m_tail);
            Array.Copy(data, index, m_buffer, m_tail, firstPart);
            m_tail += firstPart;
            if (m_tail >= MaxSize) m_tail -= MaxSize;       // 环形更新尾指针
                
            if (size > firstPart)
            {
                Array.Copy(data, index + firstPart, m_buffer, m_tail, size - firstPart);
                m_tail += size - firstPart;
                if (m_tail >= MaxSize) m_tail -= MaxSize;   // 环形更新尾指针
            }
            m_curSize += size;
            // Log.Info($"[SocketBuffer] WriteBufferUnsafe: {m_head} {m_tail} {size} {m_curSize}");
        }
        
        public int Push(byte[] data, int index = 0)
        {
            if (data == null!) throw new Exception($"Try write null data to buffer");
            bool lockCtrl = false;
            try
            {
                m_spinLock.Enter(ref lockCtrl);             // 进入自旋锁
                if (data.Length + m_sizeByte > Free) return 0;
                int size = WriteSizeUnsafe(data.Length);    // 写入大小
                WriteBufferUnsafe(data, index, data.Length);
                return size + data.Length;                  // size + data
            }
            finally
            {
                if (lockCtrl) m_spinLock.Exit();            // 退出自旋锁
            }
        }
        
        #endregion
        
        
        #region MoveHead

        private void MoveHead()
        {
            if (m_head < MaxSize >> 1) return;

            Log.Info($"[SocketBuffer] MoveHead: {m_head} {m_tail} {m_curSize}");
            if (m_tail == m_head)
            {
                Array.Copy(m_buffer, m_head, m_buffer, 0, MaxSize - m_head);
                m_head = 0;
                m_tail = 0;
                m_curSize = 0;
                return;
            }

            // 计算有效数据长度
            int dataSize = m_curSize;

            if (m_tail > m_head)
            {
                Array.Copy(m_buffer, m_head, m_buffer, 0, dataSize);
            }
            else
            {
                int tempBufferSize = m_tail + 1;
                
                if (dataSize <= MaxSize >> 1)
                {
                    // 根据需要分配栈上的临时缓冲区
                    Span<byte> tempBuffer = stackalloc byte[tempBufferSize];
                    m_buffer.AsSpan(0, tempBufferSize).CopyTo(tempBuffer);
                
                    // 复制数据
                    Array.Copy(m_buffer, m_head, m_buffer, 0, MaxSize - m_head);
                    tempBuffer.CopyTo(m_buffer.AsSpan(MaxSize - m_head, tempBufferSize)); 
                }
                else
                {
                    Array.Copy(m_buffer, 0, m_buffer, MaxSize - m_head, tempBufferSize);
                    Array.Copy(m_buffer, m_head, m_buffer, 0, MaxSize - m_head);
                }
            }
            
            // 更新指针
            m_head = 0;
            m_tail = dataSize;
        }

        #endregion
    }
}
